import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.regex.Pattern;
import java.io.FileWriter;

public class driver {
        public static void main(String[] args) throws Exception {
                //read file
                Scanner filesc=new Scanner(new File(
                                System.getProperty("user.dir") + "/src/prog.txt"));
                StringBuilder filereader=new StringBuilder();
                while(filesc.hasNextLine())
                        filereader.append(filesc.nextLine()+"\n");
                String filestring=filereader.toString();
                System.out.println(filestring);
                
                Map<String, String> tokenDescriptionMap = new TreeMap<String, String>();
                tokenDescriptionMap.put("(([a-zA-Z]\\w{0,9})|(_[a-zA-Z0-9]\\w{0,8}))",
                                "ID");
                tokenDescriptionMap.put("[0-9]+", "INTNUM");
                tokenDescriptionMap.put(":=", "ASSIGN");
                tokenDescriptionMap.put(Pattern.quote("("), "LEFTPAR");
                tokenDescriptionMap.put(Pattern.quote(")"), "RIGHTPAR");
                tokenDescriptionMap.put(Pattern.quote(","), "COMMA");
                tokenDescriptionMap.put(Pattern.quote(";"), "SEMICOLON");
                tokenDescriptionMap.put(Pattern.quote("+"), "PLUS");
                tokenDescriptionMap.put(Pattern.quote("-"), "MINUS");
                tokenDescriptionMap.put(Pattern.quote("*"), "MULTIPLY");
                tokenDescriptionMap.put(Pattern.quote("%"), "MOD");
                Scanner tokensc = new Scanner(TokenGenerator.parse(
                                filestring, tokenDescriptionMap,
                                Arrays.asList("print", "read", "begin", "end")));


					 // output tokens to text file tokensGenerated.txt
					 File tokensGenerated = new File(
                                System.getProperty("user.dir") + "/src/tokensGenerated.txt");
					 FileWriter tokenwriter = new FileWriter(tokensGenerated, false);
					 tokenwriter.flush();
					 tokenwriter.write(TokenGenerator.parse(
                                filestring, tokenDescriptionMap,
                                Arrays.asList("print", "read", "begin", "end")));
										  
					 tokenwriter.close();

                System.out.println(TokenGenerator.parse(
                                filestring, tokenDescriptionMap,
                                Arrays.asList("print", "read", "begin", "end")));
                ParserGenerator pg = new ParserGenerator(new File(
                                System.getProperty("user.dir") + "/src/grammarRule.txt"));
                for (ProductionRule stmt : pg.stmts)
                        System.out.println(stmt);
                Map<String, Set<String>> firstsets = pg.followSets;
                for (String symbol : firstsets.keySet()) {
                        StringBuilder out = new StringBuilder();
                        out.append(symbol + " : {");
                        for (String s : firstsets.get(symbol))
                                out.append(s + ", ");
                        out.append(" }\n");
                        System.out.println(out.toString());
                }


                // LL(1) parsing algorithm
                Stack<String> parsingStack = new Stack<String>();
                String nextinput = tokensc.next();
                ParsingTable table = pg.parsingtable;
					 
					 // output parsing table to text file parsingTable.txt
					 File tableFile = new File(
                                System.getProperty("user.dir") + "/src/parsingTable.txt");
					 FileWriter tableWriter = new FileWriter(tableFile, false);
					 tableWriter.flush();
					 tableWriter.write(table.toString());
					 tableWriter.close();
					 
                parsingStack.push("$");
                parsingStack.push(pg.startSymbol);
                Set<String> nonterminals = pg.getNonterminals();
                
                for(String t:nonterminals)
                        System.out.println(t);

                while ((!parsingStack.peek().equals("$")) && (!nextinput.equals("$"))) {
                        /*System.out.println("current peek: "+parsingStack.peek());
                        System.out.println(nonterminals.contains(parsingStack.peek())?"nonterminal":"terminial");
                        System.out.println("rule: "+table.get(parsingStack.peek(), nextinput));*/
                        for(String s:parsingStack)
                                System.out.println(s);
                        System.out.println(nextinput+"\n");
                        if (parsingStack.peek().equals(nextinput)) {// match
                                parsingStack.pop();
                                nextinput = tokensc.next();
                        } else if (parsingStack.peek().equals("EPSILON"))
                                parsingStack.pop();
                        else if (nonterminals.contains(parsingStack.peek())
                                        && table.get(parsingStack.peek(), nextinput) != null) {// generate
                                ProductionRule rule = table.get(parsingStack.peek(), nextinput);
                                parsingStack.pop();
                                List<String> rhs = rule.rightHandSide.get(0);
                                for (int j = rhs.size() - 1; j >= 0; j--){
                                        parsingStack.push(rhs.get(j));
                                }
                        } else {
                                System.out.println("dump stack");
                                while (!parsingStack.empty())
                                        System.out.println(parsingStack.pop());
                                System.out.println("next input: " + nextinput);
                                throw new Exception("stack error");
                        }
                }
                
                //when input ends, try to resolve non-terminal on stack using epsilon rule
                if(nextinput.equals("$")&&!parsingStack.peek().equals("$"))
                        while(nonterminals.contains(parsingStack.peek())&&table.get(parsingStack.peek(), "EPSILON")!=null)
                                parsingStack.pop();
                
                if (parsingStack.peek().equals("$") && nextinput.equals("$"))
                        System.out.println("accept");
                else
                        System.out.println("error");
        }
}

